﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenQuarters.WebQuarters.Core;
using System.Web;
using System.Web.UI;
using OpenQuarters.WebQuarters.Utils;
using System.Web.Mvc;
using System.Data.Linq;
using System.Reflection;
using System.IO;
using RssToolkit.Rss;
using System.Xml;
using System.Text.RegularExpressions;

namespace OpenQuarters.WebQuarters.Modules.ItemList
{
    public class ItemListModule : OpenQuarters.WebQuarters.Core.Module
    {
        public static Dictionary<string, string> AvailableItemTypes
        {
            get
            {
                var types = new List<Type>();
                foreach (var t in GeneralUtils.GetSubclassesOf(typeof(IItemListable), true))
                {
                    types.Add(t);
                }

                Dictionary<string, string> d = new Dictionary<string, string>();
                d.Add("", "[From List Provider]");
                types
                    .OrderBy(e => e.Name)
                    .ToList()
                    .ForEach(e => d.Add(e.FullName, e.Name.Replace("_", " ")));
                return d;
            }
        }

        public static Dictionary<string, string> AvailableViews(string data)
        {
            var d = new Dictionary<string, string>();

            d.Add("", "[None]");

            foreach (var dir in new DirectoryInfo(Oracle.Context.Server.MapPath("~/Templates/")).GetDirectories())
            {
                if (dir.Name.Contains(data))
                {
                    var listDir = dir.GetDirectories("List", SearchOption.TopDirectoryOnly);
                    if (listDir.Count() > 0)
                    {
                        foreach (var template in listDir[0].GetFiles("*.ascx", SearchOption.TopDirectoryOnly))
                        {
                            d.Add("List/" + template.Name.Replace(".ascx", ""), template.Name.Replace(".ascx", ""));
                        }
                    }
                    foreach (var template in dir.GetFiles("*.ascx", SearchOption.TopDirectoryOnly))
                    {
                        d.Add(template.Name.Replace(".ascx", ""), template.Name.Replace(".ascx", ""));
                    }
                }
            }
            return d;
        }

        private string itemType = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.AvailableItemTypes")]
        public string ItemType
        {
            get { return itemType; }
            set { itemType = value; }
        }

        public static Dictionary<string, string> QueryableListProviders
        {
            get
            {
                Dictionary<string, string> methods = new Dictionary<string, string>();

                List<MethodInfo> allMethods = GeneralUtils.GetMethodsWithAttribute<Core.QueryableListProviderAttribute, Core.QueryableListProviderContainerAttribute>();

                // Add default
                methods.Add("", "[Default]");

                foreach (MethodInfo method in allMethods)
                {
                    methods.Add(method.Module.Assembly.GetName().Name + "," + method.DeclaringType.Name + "," + method.Name, method.Name);
                }

                return methods;
            }
        }

        private string queryableListProvider = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.QueryableListProviders")]
        public string QueryableListProvider
        {
            get { return queryableListProvider; }
            set { queryableListProvider = value; }
        }

        private string summaryView = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.AvailableViews/[Form.ItemType]")]
        public string Summary_View
        {
            get { return summaryView; }
            set { summaryView = value; }
        }

        private string mainView = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.AvailableViews/[Form.ItemType]")]
        public string Main_View
        {
            get { return mainView; }
            set { mainView = value; }
        }

        private string pagerView = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.AvailableViews/[ItemType]")]
        public string Pager_View
        {
            get { return pagerView; }
            set { pagerView = value; }
        }

        private string fullViewPage = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.CMSPage)]
        public string Full_View_Page
        {
            get { return fullViewPage; }
            set { fullViewPage = value; }
        }

        private Type type
        {
            get
            {
                if (String.IsNullOrEmpty(ItemType))
                {
                    return null;
                }
                return GeneralUtils.GetTypeByName(ItemType);
            }
            set
            {
                ItemType = value.GetType().FullName;
            }
        }

        [Newtonsoft.Json.JsonProperty]
        public List<string> FilterableFields
        {
            get
            {
                return Core.FilterableAttribute.GetFilterableFields(GeneralUtils.GetTypeByName(this.ItemType));
            }
        }

        [Newtonsoft.Json.JsonProperty]
        public List<string> OrderableFields
        {
            get
            {
                return Core.OrderableAttribute.GetOrderableFields(GeneralUtils.GetTypeByName(this.ItemType));
            }
        }

        private List<Filter> filters = new List<Filter>();
        [Newtonsoft.Json.JsonProperty]
        public List<Filter> Filters
        {
            get { return filters; }
            set { filters = value; }
        }

        private List<Ordering> ordering = new List<Ordering>();
        [Newtonsoft.Json.JsonProperty]
        public List<Ordering> Ordering
        {
            get { return ordering; }
            set { ordering = value; }
        }

        private bool maintainList = false;

        [PropertyConfiguration(PropertyType.CheckBox)]
        [Newtonsoft.Json.JsonProperty]
        public bool Maintain_List
        {
            get { return maintainList; }
            set { maintainList = value; }
        }

        private int itemsPerPage = 5;

        [PropertyConfiguration(PropertyType.TextBox)]
        [Newtonsoft.Json.JsonProperty]
        public int Items_Per_Page
        {
            get { return itemsPerPage; }
            set { itemsPerPage = value; }
        }

        private int currentPage = 1;

        public int Current_Page
        {
            get { return currentPage; }
            set { currentPage = value; }
        }

        private PaginationMode paginationMode = PaginationMode.Number_List;

        [PropertyConfiguration(PropertyType.DropDown)]
        [Newtonsoft.Json.JsonProperty]
        public PaginationMode Pagination_Mode
        {
            get { return paginationMode; }
            set { paginationMode = value; }
        }

        private PaginationPosition paginationPosition = PaginationPosition.Top;

        [PropertyConfiguration(PropertyType.DropDown)]
        [Newtonsoft.Json.JsonProperty]
        public PaginationPosition Pagination_Position
        {
            get { return paginationPosition; }
            set { paginationPosition = value; }
        }

        public override void Initialise()
        {
            this.ModuleName = "Item List";
            this.ModuleDescription = "Places a customisable list of items on the page.";
        }

        public override string GetJQueryEditorName()
        {
            return "cmsModuleEditor, cmsModuleEditor_ItemList";
        }

        public ItemListerConfiguration Configuration
        {
            get
            {
                return new ItemListerConfiguration()
                {
                    Filters = filters,
                    Ordering = ordering,
                    SummaryView = summaryView,
                    MainView = mainView,
                    PaginationView = pagerView,
                    FullViewPage = fullViewPage,
                    MaintainList = maintainList,
                    ItemListerId = this.ModuleId,
                    Items_Per_Page = this.Items_Per_Page,
                    Pagination_Mode = this.Pagination_Mode,
                    Pagination_Position = this.Pagination_Position,
                    Current_Page = this.currentPage,
                    Method = this.QueryableListProvider
                };
            }
        }

        public Control ItemLister = null;

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (type != null)
            {
                //Get current page
                if (Regex.IsMatch(Request.Url.AbsoluteUri.ToString(), @"/page-\d*(\/||$)"))
                {
                    Match pageMatches = Regex.Match(Request.Url.AbsoluteUri.ToString(), @"/page-(\d*)(\/||$)");
                    currentPage = Convert.ToInt32(pageMatches.Groups[1].Value);
                }
                else if (!string.IsNullOrEmpty(Request.Form["cms_CurrentPage"]))
                {
                    try
                    {
                        currentPage = Convert.ToInt16(Request.Form["cms_CurrentPage"]);
                    }
                    catch (FormatException) { }
                }

                //Get items per page
                if (Regex.IsMatch(Request.Url.AbsoluteUri.ToString(), @"/all(\/||$)"))
                {
                    currentPage = 0;
                    itemsPerPage = 0;
                }
                else if (!string.IsNullOrEmpty(Request.Form["cms_ItemsPerPage"]))
                {
                    try
                    {
                        itemsPerPage = Convert.ToInt16(Request.Form["cms_ItemsPerPage"]);
                    }
                    catch (FormatException) { }
                }

                ItemLister = (Control)(typeof(ItemListerControl<>).MakeGenericType(new Type[] { type })).GetConstructor(new Type[] { typeof(ItemListerConfiguration) }).Invoke(new object[] { Configuration });

                //Check if request for rss feed
                if (Request.Url.AbsolutePath.Length > 6 && Request.Url.AbsolutePath.Substring(Request.Url.AbsolutePath.Length - 7) == "rss.xml")
                {
                    List<IItemListable> items = (List<IItemListable>)ItemLister.GetType().GetMethod("GetItemsAsIItemListable").Invoke(ItemLister, null);

                    RssDocument rssDoc = new RssDocument();
                    rssDoc.Channel = new RssChannel();

                    rssDoc.Channel.Title = Oracle.GetSettings().SiteRssTitle;
                    rssDoc.Channel.Items = new List<RssToolkit.Rss.RssItem>();

                    foreach (var item in items)
                    {
                        rssDoc.Channel.Items.Add(item.ToRssItem());
                    }

                    string outputXml = rssDoc.ToXml(DocumentType.Rss);
                    XmlDocument document = new XmlDocument();
                    document.LoadXml(outputXml);
                    Oracle.Response.ContentType = "text/xml";
                    Oracle.Response.Clear();
                    document.Save(Oracle.Response.OutputStream);
                    Oracle.Response.End();

                }
                else
                {
                    this.Controls.Add(ItemLister);
                }

            }
            else
            {
                this.Controls.AddLiteral("<p>Double-click to select an item type</p>");
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (Core.User.Current.HasPermission(Permissions.CMSPage.Edit.SetValue(CMSPage.Current.PageId.ToString())))
            {
                // We are using the content module editor with a custom onSetup and onSave methods for inline editing of entity fields
                JavaScript.RegisterScript("Modules.Content.AdminScript.js", "OpenQuarters.WebQuarters.Modules.Core", "Resources.Content.AdminScript.js", JavaScriptType.AdminScript);

                JavaScript.RegisterScript("Modules.ItemList.ItemListEditor.js", "OpenQuarters.WebQuarters.Modules.ItemList", "Resources.Scripts.ItemListEditor.js", JavaScriptType.AdminScript);
                CSS.RegisterStyleSheet("Modules.ItemList.ItemListEditor.css", "OpenQuarters.WebQuarters.Modules.ItemList", "Resources.Css.ItemListEditor.css", CSSType.AdminStyleSheet);

                if (ItemLister != null)
                {
                    var targetItem = ItemLister.GetType().GetProperty("TargetItem").GetValue(ItemLister, null);
                    this.Controls.AddAt(0, new LiteralControl(@"<textarea style=""display:none;"" class=""additionalConfig"">{ FullViewMode: " + (targetItem != null ? "true" : "false") + " }</textarea>"));
                }
            }
        }

        public static string GetItemEditLink<TItemType>(TItemType item) where TItemType : IItemListable
        {
            if (User.Current.HasPermission(Core.Permissions.Entity.Edit.SetValue(item.GetType().FullName)))
            {
                string elementID = item.GetType().FullName.Replace(".", "_") + "_" + item.GetIDAsString();
                return @"<a href=""javascript:void(0);"" class=""cmsEditLink"" rel=""" + elementID + @""">" + Translation.Translate("Edit") + @"</a>";
            }
            return "";
        }

        public static string GetItemDeleteLink<TItemType>(TItemType item) where TItemType : IItemListable
        {
            if (User.Current.HasPermission(Core.Permissions.Entity.Delete.SetValue(item.GetType().FullName)))
            {
                string elementID = item.GetType().FullName.Replace(".", "_") + "_" + item.GetIDAsString();
                return @"<a href=""javascript:void(0);"" class=""cmsDeleteLink"" rel=""" + elementID + @""">" + Translation.Translate("Delete") + @"</a>";
            }
            return "";
        }

        private static void setupRequest(out Type entityType, out IItemListable obj)
        {
            entityType = Utils.GeneralUtils.GetTypeByName(Oracle.Request.Form["EntityType"]);
            if (entityType == null)
            {
                throw new ArgumentException("EntityType '" + Oracle.Request.Form["EntityType"] + "' could not be found");
            }

            obj = (IItemListable)Activator.CreateInstance(entityType);
            if (obj == null)
            {
                throw new ArgumentException("Could not create instance of type '" + Oracle.Request.Form["EntityType"] + "'");
            }
        }

        [Invokable]
        public static void Save()
        {
            IItemListable obj = null;
            Type entityType = null;
            setupRequest(out entityType, out obj);

            bool bAuthorised = false;

            if (obj.IsNewItem())
            {
                bAuthorised = Core.User.Current.HasPermission(Permissions.Entity.Create.SetValue(entityType.FullName));
            }
            else
            {
                bAuthorised = Core.User.Current.HasPermission(Permissions.Entity.Edit.SetValue(entityType.FullName));
            }

            if (!bAuthorised)
            {
                throw new System.InvalidOperationException("Not authorised to perform this operation");
            }

            obj = (IItemListable)Utils.GeneralUtils.FromJSON(Oracle.Request.Form["Entity"].Base64Decode(), obj, entityType);
            obj = obj.LoadItem(obj.GetIDAsString());
            obj = (IItemListable)Utils.GeneralUtils.FromJSON(Oracle.Request.Form["Entity"].Base64Decode(), obj, entityType);
            obj = obj.SaveItem();

            var draftItems = ItemListerConfiguration.GetDraftItems(Oracle.Request.Form["ModuleId"]);
            draftItems.Clear();
            draftItems.Add(new ItemDefinition(obj));

            ItemListerHelper.CurrentFullViewPage = CMSPage.GetVersionForUser(Oracle.Request.Form["Url"]).CMSPage;
            var saveResponse = new ItemListSaveResponse()
            {
                itemUrl = ItemListerHelper.GetItemUrl(obj)
            };

            Oracle.Response.Write(saveResponse.ToJSON());
            Oracle.Response.End();
        }

        [Invokable]
        public static void Delete()
        {
            IItemListable obj = null;
            Type entityType = null;
            setupRequest(out entityType, out obj);

            if (!Core.User.Current.HasPermission(Permissions.Entity.Delete.SetValue(entityType.FullName)))
            {
                throw new System.InvalidOperationException("Not authorised to perform this operation");
            }

            obj = (IItemListable)Utils.GeneralUtils.FromJSON(Oracle.Request.Form["Entity"].Base64Decode(), obj, entityType);

            var draftItems = ItemListerConfiguration.GetDraftItems(Oracle.Request.Form["ModuleId"]);
            if (draftItems.FirstOrDefault(i => i.Item.GetIDAsString() == obj.GetIDAsString() && i.type == obj.GetType().FullName) != null)
            {
                draftItems.Clear();
            }
            else
            {
                obj = obj.LoadItem(obj.GetIDAsString());
                obj.DeleteItem();
            }
        }
    }
}
